////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                            //
//                                                         WNProject                                                          //
//                                                                                                                            //
//            This file is distributed under the BSD 2-Clause open source license. See License.txt for details.               //
//                                                                                                                            //
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#pragma once

#ifndef __WN_MATH_INTERNAL_X86_SSE2_ELEMENTS_H__
#define __WN_MATH_INTERNAL_X86_SSE2_ELEMENTS_H__

#ifndef __WN_MATH_INTERNAL_ELEMENTS_H__
    #error "Internal/x86/SSE2/WNElements.h should never be included directly."
#elif !defined _WN_X86
    #error "Internal/x86/SSE2/WNElements.h has been included on a non x86 platform. Please rectify this."
#elif !defined __WN_MATH_ENABLE_EXTENSIONS
    #error "Internal/x86/SSE2/WNElements.h has been included when extensions are disabled. Please rectify this."
#elif !defined __WN_MATH_SSE2_ALLOWED
    #error "Internal/x86/SSE2/WNElements.h has been included when SSE2 is not allowed. Please rectify this."
#endif

#include <emmintrin.h>

namespace WNMath {
    namespace __WNInternal {
        #ifdef __WN_MATH_SSE2_AVAILABLE
            #ifdef __WN_MATH_AVX_AVAILABLE
                template <WN_UINT32 Dimension>
                struct WN_ALIGN(16) __WNElements<WN_FLOAT64, Dimension, typename  WNCore::WNEnableWhen<(Dimension > 1 && Dimension <= 2)>::Value> {
                    WN_STATIC_ASSERT_DESC(Dimension > 0, "Must have an element base of at least 1");

                    union {
                        WN_FLOAT64 mValues[Dimension];
                        __m128d mXMMValues[(Dimension + 1) >> 1];
                    };
                };
            #else
                template <WN_UINT32 Dimension>
                struct WN_ALIGN(16) __WNElements<WN_FLOAT64, Dimension, typename  WNCore::WNEnableWhen<(Dimension > 1)>::Value> {
                    WN_STATIC_ASSERT_DESC(Dimension > 0, "Must have an element base of at least 1");

                    union {
                        WN_FLOAT64 mValues[Dimension];
                        __m128d mXMMValues[(Dimension + 1) >> 1];
                    };
                };
            #endif
        #else
            #ifdef __WN_MATH_AVX_ALLOWED
                template <WN_UINT32 Dimension>
                struct WN_ALIGN(16) __WNElements<WN_FLOAT64, Dimension, typename WNCore::WNEnableWhen<(Dimension == 2)>::Value> {
                    WN_STATIC_ASSERT_DESC(Dimension > 0, "Must have an element base of at least 1");

                    WN_FLOAT64 mValues[Dimension];
                };

                template <WN_UINT32 Dimension>
                struct WN_ALIGN(32) __WNElements<WN_FLOAT64, Dimension, typename WNCore::WNEnableWhen<(Dimension > 2)>::Value> {
                    WN_STATIC_ASSERT_DESC(Dimension > 0, "Must have an element base of at least 1");

                    WN_FLOAT64 mValues[Dimension];
                };
            #else
                template <WN_UINT32 Dimension>
                struct WN_ALIGN(16) __WNElements<WN_FLOAT64, Dimension, typename WNCore::WNEnableWhen<(Dimension > 1)>::Value> {
                    WN_STATIC_ASSERT_DESC(Dimension > 0, "Must have an element base of at least 1");

                    WN_FLOAT64 mValues[Dimension];
                };
            #endif
        #endif
    }
}

#endif // __WN_MATH_INTERNAL_X86_SSE2_ELEMENTS_H__